﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Sherwood.SignOn.Server.Models;
using Sherwood.SignOn.Server.Models.ViewModels;
using Sherwood.SignOn.Server.Controllers;

using System.Web;
using System.Web.Mvc;
using Sherwood.SignOn.Server.Models.Repositories;
using Sherwood.SignOn.Server.Models.Services;

namespace Sherwood.SignOn.Server.Tests
{
    [TestClass]
    public class RegisterTests : TestBase
    {
        /// <summary>
        /// Tests creation of a new, valid user account (which should succeed)
        /// </summary>
        [TestMethod]
        public void CreateUserAccountTest()
        {
            //Arrange

            UserAccountController controller = new UserAccountController();
            RegisterViewModel model = new RegisterViewModel();
            model.UserName = "myUsername";
            model.Password = "myPassword";
            model.Password2 = "myPassword";
            model.FirstName = "John";
            model.LastName = "Smith";
            model.Email = "rvanoord@allmail.net";
            model.CultureName1 = "en-US";
            model.CultureName2 = "en-US";
            model.CultureName3 = "en-US";
            model.DateOfBirth = new DateTime(2010,1,1);
            model.Gender = Gender.Male;
            model.Country = "US";

            //Act
            ViewResult result = controller.Register(model) as ViewResult;
            IUserAccount account = Data.UserAccounts.GetUserAccountByUsername(model.UserName);

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.ViewName.Equals("RegisterConfirmation"), "ViewName ('" + result.ViewName + "') is not equal to 'RegisterConfirmation'");
            Assert.IsNotNull(account);
            Assert.AreEqual<string>(model.UserName, account.UserName);
            Assert.AreEqual<string>(model.FirstName, account.FirstName);
            Assert.AreEqual<string>(model.LastName, account.LastName);
            Assert.AreEqual<string>(model.Email, account.Email);
            Assert.AreEqual<DateTime?>(model.DateOfBirth, account.DateOfBirth);
            Assert.AreEqual<Gender>(model.Gender, account.Gender);
            Assert.AreNotEqual<string>(model.Password, account.PasswordHash);
            Assert.IsFalse(string.IsNullOrEmpty(account.PasswordHash));
            Assert.AreEqual<string>(model.Country, account.Country);
            Assert.AreEqual<string>(model.CultureName1, account.CultureName1);

        }

        /// <summary>
        /// Tests creation of an invalid account (which should not be permitted)
        /// </summary>
        [TestMethod]
        public void CreateInvalidUserAccountTest()
        {
            //Arrange

            UserAccountController controller = new UserAccountController();
            RegisterViewModel model = new RegisterViewModel();
            model.UserName = "";
            model.Password = "";
            model.Password2 = "";
            model.FirstName = "";
            model.LastName = "";
            model.Email = "rvanoord@allmailnet";
            model.CultureName1 = "";
            model.DateOfBirth = new DateTime(1, 1, 1);
            model.Gender = Gender.Unspecified;
            model.Country = "";

            //Act
            ViewResult result = controller.Register(model) as ViewResult;
            ModelStateDictionary state = controller.ModelState;

            //Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(TestHelper.HasError("UserName", state));
            Assert.IsTrue(TestHelper.HasError("Password", state));
            Assert.IsTrue(TestHelper.HasError("Email", state));
            Assert.IsTrue(TestHelper.HasError("CultureName1", state));

            //Should not cause validation errors (non-required fields)
            Assert.IsFalse(TestHelper.HasError("Country", state));
            Assert.IsFalse(TestHelper.HasError("Gender", state));
            Assert.IsFalse(TestHelper.HasError("DateOfBirth", state));

        }

        /// <summary>
        /// Tests creation of duplicate accounts (which should not be permitted)
        /// </summary>
        [TestMethod]
        public void CreateDuplicateAccountTest()
        {
            //Arrange

            UserAccountController controller = new UserAccountController();
            RegisterViewModel model = new RegisterViewModel();
            model.UserName = "myUsername";
            model.Password = "myPassword";
            model.Password2 = "myPassword";
            model.FirstName = "John";
            model.LastName = "Smith";
            model.Email = "rvanoord@allmail.net";
            model.CultureName1 = "en-US";
            model.CultureName2 = "en-US";
            model.CultureName3 = "en-US";
            model.DateOfBirth = new DateTime(2010, 1, 1);
            model.Gender = Gender.Male;
            model.Country = "US";

            //Act (Existing username)
            ViewResult result = controller.Register(model) as ViewResult;
            model.Email = "rvanoord2@allmail.net"; 
            ViewResult result2 = controller.Register(model) as ViewResult;
            int accountCount = (from a in Data.UserAccounts.GetAllAccounts() where a.UserName == model.UserName select a).Count();
            ModelStateDictionary state = controller.ModelState;

            //Assert
            Assert.IsNotNull(result);
            Assert.IsNotNull(result2);
            //Some error message should be displayed
            Assert.IsTrue(TestHelper.HasError(state)); 
            //Only one account should have been created
            Assert.IsTrue(accountCount == 1);


            //Act (Existing verified acount with same email)
            controller = new UserAccountController();
            model.UserName = "myUserName2";
            model.Email = "rvanoord@allmail.net"; //test duplicate email.
            IUserAccount existingAccount1 = Data.UserAccounts.GetUserAccountByEmail(model.Email);
            existingAccount1.SetVerificationParameters(VerificationStatus.Verified, null, null);
            ViewResult result3 = controller.Register(model) as ViewResult;
            accountCount =  (from a in Data.UserAccounts.GetAllAccounts() where a.Email.Equals(model.Email) select a).Count();


            //Assert
            Assert.IsNotNull(result3);
            //Some error message should be displayed
            Assert.IsTrue(TestHelper.HasError(state));
            //Only one account should have been created
            Assert.IsTrue(accountCount == 1, "Account count should be 1, but is actually " + accountCount);


            //Act (Existing unverified account with same email)
            controller = new UserAccountController();
            model.UserName = "myUserName2";
            model.Email = "rvanoord@allmail.net"; //test duplicate email.
            IUserAccount existingAccount2 = Data.UserAccounts.GetUserAccountByEmail(model.Email);
            existingAccount2.SetVerificationParameters(VerificationStatus.Unverified, existingAccount2.RequestedUrl, existingAccount2.VerificationCode);
            ViewResult result4 = controller.Register(model) as ViewResult;
            accountCount =  (from a in Data.UserAccounts.GetAllAccounts() where a.Email.Equals(model.Email) select a).Count();
            //Reload account
            existingAccount2 = Data.UserAccounts.GetUserAccountByEmail(model.Email);

            //Assert
            Assert.IsNotNull(result4);
            //Some error message should be displayed
            Assert.IsTrue(TestHelper.HasError(state));
            //Only one account should have been created
            Assert.IsTrue(accountCount == 1, "Account count should be 1, but is actually " + accountCount);
            Assert.IsTrue(existingAccount2.UserName.Equals(model.UserName), "Existing, unverified account should have been updated.!");
        }
    }
}
